package org.m8.auth.handler.miniapp;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import org.m8.auth.handler.social.SocialAuthLoginRespVO;
import org.m8.auth.config.SocialConfig;
import org.m8.auth.config.SocialTypeEnum;
import org.m8.auth.model.SocialUserBindDO;
import org.m8.auth.model.SocialUserBindService;
import org.m8.auth.handler.AbstractAuthLoginHandler;
import org.m8.auth.handler.AuthLoginHandlerRegistry;
import org.m8.auth.controller.vo.AuthLoginRespVO;
import org.m8.auth.handler.social.SocialAuthSocialLoginReqVO;
import org.m8.user.model.User;
import org.m8.user.model.UserService;
import org.m8.util.JsonUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Executors;

import static org.m8.util.exception.ServiceExceptionUtil.exception;
import static org.m8.util.exception.emnus.GlobalErrorCodeConstants.SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR;

/**
 * 社交登录-微信小程序登录 带手机号
 *
 * @author yidanjun
 * @date 2024/9/7
 **/
@Slf4j
@Service
public class SocialMiniAppWithPhoneAuthLoginHandler extends AbstractAuthLoginHandler {

    private static final String AUTH_LOGIN_TYPE = "social_mini_app_phone";

    /**
     * 缓存 WxMaService 对象
     *
     * 说明同 {@link #wxMaServiceCache} 变量
     */
    private final LoadingCache<String, WxMaService> wxMaServiceCache =
            CacheBuilder.newBuilder()
                    // 只阻塞当前数据加载线程，其他线程返回旧值
                    .refreshAfterWrite(Duration.ofSeconds(10L))
                    // 通过 asyncReloading 实现全异步加载，包括 refreshAfterWrite 被阻塞的加载线程
                    .build(CacheLoader.asyncReloading(new CacheLoader<String, WxMaService>() {
                        @Override
                        public WxMaService load(String key) {
                            String[] keys = key.split(":");
                            return buildWxMaService(keys[0], keys[1]);
                        }

                    }, Executors.newCachedThreadPool()));


    @Resource
    private SocialUserBindService socialUserBindService;

    @Resource
    private UserService userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private SocialConfig socialConfig;

    @Override
    protected void init() {
        AuthLoginHandlerRegistry.getInstance().register(AUTH_LOGIN_TYPE, this);

    }

    @Override
    public < R extends AuthLoginRespVO> R login(Map<String, Object> req) {
        SocialAuthSocialLoginReqVO socialAuthSocialLoginReqVO = JsonUtil.parse(req, SocialAuthSocialLoginReqVO.class);
        return (R)weixinMiniAppLogin(socialAuthSocialLoginReqVO.getCode(), socialAuthSocialLoginReqVO.getState(), socialAuthSocialLoginReqVO.getPhoneCode());
    }


    private SocialAuthLoginRespVO weixinMiniAppLogin(String code, String state, String phoneCode) {
        // 获得对应的手机号信息
        WxMaPhoneNumberInfo phoneNumberInfo = this.getWxMaPhoneNumberInfo(phoneCode);
        Assert.notNull(phoneNumberInfo, "获得手机信息失败，结果为空");

        User user = userService.getByMobile(phoneNumberInfo.getPurePhoneNumber());

        if (user == null) {
            user = new User();
            user.setUsername("w_" + RandomUtil.randomString(10));
            user.setName(user.getUsername());
            user.setMobile(phoneNumberInfo.getPhoneNumber());
            userService.addUser(user);
        }

        SocialUserBindDO socialUserBindDO = socialUserBindService.authSocialUser(SocialTypeEnum.WECHAT_MINI_APP.getType(), code, state, user.getUsername());
        socialUserBindDO.setUserId(user.getUsername());
        socialUserBindDO.setUpdateTime(new Date());
        socialUserBindService.bindUser(socialUserBindDO);

        return createTokenAfterLoginSuccess(user, socialUserBindDO);
    }

    public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(String phoneCode) {
        WxMaService service = getWxMaService();
        try {
            return service.getUserService().getPhoneNoInfo(phoneCode);
        } catch (WxErrorException e) {
            log.error("[getPhoneNoInfo][ phoneCode({}) 获得手机号失败]", phoneCode, e);
            throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
        }
    }

    /**
     * 获得 clientId + clientSecret 对应的 WxMpService 对象
     *
     * @return WxMpService 对象
     */
    WxMaService getWxMaService() {
        return wxMaServiceCache.getUnchecked(socialConfig.getClientId() + ":" + socialConfig.getClientSecret());
    }

    /**
     * 创建 clientId + clientSecret 对应的 WxMaService 对象
     *
     * @param clientId     微信小程序 appId
     * @param clientSecret 微信小程序 secret
     * @return WxMaService 对象
     */
    private WxMaService buildWxMaService(String clientId, String clientSecret) {
        // 第一步，创建 WxMaRedisBetterConfigImpl 对象
        WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl(
                new RedisTemplateWxRedisOps(stringRedisTemplate),
                "m8ma");
        configStorage.setAppid(clientId);
        configStorage.setSecret(clientSecret);

        // 第二步，创建 WxMpService 对象
        WxMaService service = new WxMaServiceImpl();
        service.setWxMaConfig(configStorage);
        return service;
    }


}
